import fs from 'fs';
import path from 'path';
import axios from 'axios';
import csv from 'csvtojson';
import * as _ from 'lodash';
import { SimulationConfigCache } from './cache';
import { getUploader } from './uploader';
import { Validator } from './validator';
import { zipEntriesSchema } from './validation/schema';
import { ZipEntries } from './zipEntries';
import * as util from './util';
import express from 'express';
import { SimJobStatusName, SimJobStatus } from './simulator';
import moment from 'moment';
import { objectStorageService } from "./objectStorage";
import { configurationService } from "./configService";
import { getTimeLeft2BySecond } from "./util";
var AdmZip = require("adm-zip");


let currentUploadProgress: number = 0;

export class SimIdentity {
  private static _identity: string;

  public static set(identity: string) {
    this._identity = identity;
  }

  public static get() {
    return this._identity;
  }
}

let uploadPath = '/tmp/upload';
if (util.isDesktop()) {
  uploadPath = `${process.env.tmp}/sh/upload`.replace(/\\/g, '/');
}
const zipUpload = getUploader(uploadPath);
const csvUpload = getUploader(uploadPath);
const paramUpload = getUploader(uploadPath);
const zip = new ZipEntries();

if (!fs.existsSync(uploadPath)) {
  fs.mkdirSync(uploadPath);
}

export function simConfigServerApi(app: express.Application) {

  app.use(express.static(path.join(__dirname, 'statics')));

  // 获取仿真设置
  app.get('/simulation/config', (req, res) => {
    res.json({
      success: true,
      data: SimulationConfigCache.get() || ''
    });
  });

  // 获取可用的仿真服务器
  app.get('/simulation/servers/:simType', async (req, res) => {
    const simType = req.params.simType;

    console.info(`simType = ${simType}`);
    try {
      const agentsRes = await axios.get(util.getOnlineAgent(simType));

      const { success, agents } = agentsRes.data;
      if (!success) {
        res.json({
          success: false,
          message: `Get Simulation Server Error, ${agentsRes.status}`
        });
        return;
      }
      let servers = [];
      agents.forEach(agent => {
        servers.push({
          identity: agent.identity,
          role: agent.role,
          runningCount: agent.runningJob || 0,
          queuingCount: agent.queuingJob || 0,
          cpu: agent.cpu,
          drive: agent.drive,
          memory: agent.memory,
          freeStatus: agent.cpu?.usage > 70 ? '拥挤' : '空闲'
        });
      });
      res.json({
        success: true,
        data: servers
      });
    } catch (e) {
      res.json({
        success: false,
        data: e.message
      });
    }
  });

  // check agent password
  app.post('/simulation/agent/check', async (req, res) => {
    const { identity, password } = req.body;
    const gatewayRes = await axios.post(util.getCheckAgentApi(), {
      identity,
      password
    });
    const { success, data, message } = gatewayRes.data;
    res.json({
      success,
      data,
      message
    });
  });

  // 上传仿真项目文件
  app.post('/simulation/file/zip', (req, res, next) => {
    zipUpload.single('sim_zip_file')(req, res, (err) => {
      if (err) {
        console.log('Multer error:', err);

        // 处理文件大小超限错误
        if (err.code === 'LIMIT_FILE_SIZE') {
          return res.json({
            success: false,
            message: `File size too large. Maximum allowed size is ${formatFileSize(1024 * 1024 * 1024)} (1GB)`
          });
        }

        // 处理其他multer错误
        return res.json({
          success: false,
          message: `Upload error: ${err.message}`
        });
      }

      // 没有错误，继续处理
      next();
    });
  }, async (req, res, next) => {
    console.log('upload file:', req.file);

    if (!req.file) {
      res.json({ success: false, message: 'upload file is empty' });
      return;
    }

    try {
      // 支持的文件类型
      const validExtensions = ['.zip', '.bdf'];
      const originalExtension = path.extname(req.file.originalname).toLowerCase();

      // 检查文件类型
      if (!validExtensions.includes(originalExtension)) {
        return res.json({
          success: false,
          message: 'Invalid file type. Only .zip or .bdf files are allowed'
        });
      }

      let finalPath = req.file.path;
      let finalName = req.file.originalname;
  
      // 如果是BDF文件则转换为ZIP
      if (originalExtension === '.bdf') {
        const zipFileName = finalName.replace(/\.bdf$/i, '.zip');
        const zipPath = path.join(path.dirname(finalPath), zipFileName);

        // 创建新的ZIP压缩包
        const admZip = new AdmZip();
        admZip.addFile(path.basename(finalName), fs.readFileSync(finalPath));
        admZip.writeZip(zipPath);

        // 更新为新的ZIP文件
        finalPath = zipPath;
        finalName = zipFileName;
      }

      const ossPath = util.getOSSSimulationProjectFilePath();
      let ossFile = util.convertToUnixPath(path.join(ossPath, finalName));

      // 创建自定义上传函数以跟踪进度
      await uploadFileWithProgress(finalPath, ossFile);

      zip.reset();

      res.json({
        success: true,
        data: {
          oss: ossFile,
          name: finalName
        }
      });

      // 定时清理进度信息
      setTimeout(() => {
        currentUploadProgress = 0;
      }, 30000);

    } catch (e) {
      console.log('in file/zip:', e);
      currentUploadProgress = 0;

      res.json({
        success: false,
        message: e.message
      });
    }
  });

  // 自定义上传函数以跟踪进度
  async function uploadFileWithProgress(filePath: string, ossFile: string) {
    const fileStats = fs.statSync(filePath);
    const totalSize = fileStats.size;

    // 使用真实的上传进度回调
    await objectStorageService.fPutObjectWithProgress(
      configurationService.config.objectStorage.config.bucket,
      ossFile,
      path.normalize(filePath),
      (percentage: number) => {
        console.log(`上传进度: ${percentage}%`);
        currentUploadProgress = percentage;
      }
    );
  }

  // 浏览仿真项目文件压缩包
  app.get('/simulation/file/entries', async (req, res) => {
    const validator = Validator.of(zipEntriesSchema);
    let params = !_.isEmpty(req.body) ? req.body : req.query;
    const { error, warning, value: zipFile } = validator.validate(params);
    if (error) {
      res.json({ success: false, message: error.message });
      return;
    }

    try {
      const filename = path.parse(zipFile.ossFile).base;
      const tmpFile = path.join(uploadPath, filename);

      await objectStorageService.fGetObject(configurationService.config.objectStorage.config.bucket, util.convertToUnixPath(zipFile.ossFile), path.normalize(tmpFile));

      let files = await zip.getEntries(tmpFile, zipFile.folder, zipFile.ext);
      res.json({
        success: true,
        data: { files: files }
      });
    } catch (e) {
      console.log('in file/entries:', e);
      res.json({
        success: false,
        message: e.message
      });
    }
  });

  // 上传仿真参数 csv 文件
  app.post('/simulation/file/input_csv', csvUpload.single('sim_csv_file'), async (req, res, next) => {
    console.log('upload file:', req.file);

    if (!req.file) {
      res.json({ success: false, message: 'only support .csv file' });
      return;
    }

    try {
      const ossPath = util.getOSSSimulationParameterFilePath();
      let ossFile = util.convertToUnixPath(path.join(ossPath, req.file.originalname));
      await objectStorageService.fPutObject(configurationService.config.objectStorage.config.bucket, ossFile, path.normalize(req.file.path));

      await objectStorageService.fGetObject(configurationService.config.objectStorage.config.bucket, ossFile, path.normalize(req.file.path));

      const params = await csv().fromFile(path.normalize(req.file.path));

      res.json({
        success: true,
        data: { 'oss': ossFile, name: req.file.originalname, 'params': params }
      });
    } catch (e) {
      console.log('in file/input_csv:', e);
      res.json({
        success: false,
        message: e.message
      });
    }
  });

  // 上传仿真参数文件
  app.post('/simulation/file/input', paramUpload.single('sim_param_file'), async (req, res, next) => {
    console.log('upload file:', req.file);

    if (!req.file) {
      res.json({ success: false, message: 'param file is null' });
      return;
    }

    try {
      const ossPath = util.getOSSSimulationParameterFilePath();
      let ossFile = util.convertToUnixPath(path.join(ossPath, util.generateUUID(), req.file.originalname));
      await objectStorageService.fPutObject(configurationService.config.objectStorage.config.bucket, ossFile, path.normalize(req.file.path));

      res.json({
        success: true,
        data: { 'oss': ossFile, name: req.file.originalname }
      });
    } catch (e) {
      console.log('in file/input param:', e);
      res.json({
        success: false,
        message: e.message
      });
    }
  });

  // 仿真任务停止
  app.get('/simulation/stop/:jobId', async (req, res) => {
    const jobId = req.params.jobId;
    const modelBack = Boolean(req.query.modelBack || false);
    const api = util.getGatewayJobStopApiByJobId(jobId);

    try {
      const jobStopRes = await axios.post(api, { 'modelBack': modelBack });

      const { success, message } = jobStopRes.data;

      if (success) {
        res.json({
          success: true
        });
      } else {
        res.json({
          success: false,
          message: message
        });
      }
    } catch (error) {
      console.log(`/simulation/stop error: ${error}`);
      res.json({
        success: false,
        message: error.message
      });
    }
  });

  // 获取节点运行仿真任务列表
  app.post('/simulation/jobs', async (req, res) => {
    const { businessId, status, startTime, endTime, currentPage, pageSize } = req.body;
    const jobsApi = util.getGatewayJobsPageApi();
    try {
      let statuses;
      if (status) {
        statuses = [status];
        if (status === 'queuing') {
          statuses = [SimJobStatus.created, SimJobStatus.agentAssigned, SimJobStatus.agentDispatched];
        }
      }
      const jobsRes = await axios.post(jobsApi, {
        currentPage: currentPage || 1,
        pageSize: pageSize || 10,
        businessId: businessId,
        statuses: statuses,
        startTime: startTime,
        endTime: endTime
      });

      const { success, data } = jobsRes.data;
      if (!success) {
        res.json({
          success: false,
          message: 'get jobs from gateway failed'
        });
        return;
      }

      const jobs = data.data;
      const jobList = handleJobs(jobs);
      // 分页数据
      const totalPages = data.totalPages;
      const total = data.total;

      res.json({
        success: true,
        data: {
          data: jobList,
          totalPages,
          total
        }
      });
    } catch (error) {
      console.log(`/simulation/jobs error: ${error}`);
      res.json({
        success: false,
        message: error.message
      });
    }
  });

  function handleJobs(jobs) {
    const jobList = [];
    if (jobs) {
      jobs.forEach((job, index) => {
        let runTime = '0';
        if (job.finishedAt) {
          const interval = moment(job.finishedAt).diff(moment(job.startedAt));
          runTime = getTimeLeft2BySecond(interval / 1000);
        } else if (job.startedAt) {
          const interval = moment().diff(moment(job.startedAt));
          runTime = getTimeLeft2BySecond(interval / 1000);
        }

        jobList.push({
          id: job.id,
          gatewayJobId: job.id,
          businessId: job.businessId,
          submitAt: moment(job.submitAt).format('yyyy-MM-DD HH:mm:ss'),
          agentId: job.agentId,
          status: job.status,
          statusName: SimJobStatusName[job.status],
          desc: job.desc,
          runTime: runTime,
          message: job.message,
          result: job.result,
          progress: job.progress,
          logOssPath: `${util.getJobLogPath(job.id)}`
        });
      });
    }
    return jobList;
  }

  // 获取仿真服务器上模型文件
  app.get('/simulation/model/file/trees', async (req, res) => {
    const { serverIdentity, modelPath } = req.query;
    const gatewayApi = util.getGatewayParseModelTreesApi();
    try {
      const parseModelRes = await axios.post(gatewayApi, {
        serverIdentity: serverIdentity,
        modelPath: modelPath
      });

      const { success, message, data } = parseModelRes.data;

      if (success) {
        res.json({
          success: true,
          data: data
        });
      } else {
        res.json({
          success: false,
          message: `parse model entry error: ${message}`
        });
      }
    } catch (error) {
      console.log(`/simulation/model/file/trees error: ${error}`);
      res.json({
        success: false,
        message: error.message
      });
    }
  });

  // 生成默认仿真结果本地保存路径
  app.get('/simulation/default/specified/path/:simType', async (req, res) => {
    const simType = req.params.simType;
    const defaultPath = `~/.szls/simResult/${simType}/${Date.now()}`;
    res.json({
      success: true,
      data: defaultPath
    });
  });

  // 删除任务
  app.get('/simulation/delete/job/:jobId', async (req, res) => {
    const jobId = req.params.jobId;
    const gatewayApi = util.getGatewayDeleteJobBatchByJobIdApi();
    try {
      const gatewayRes = await axios.post(gatewayApi, {
        jobIds: [jobId]
      });
      const { success, message } = gatewayRes.data;

      if (success) {
        res.json({
          success: true
        });
      } else {
        res.json({
          success: false,
          message: message
        });
      }
    } catch (error) {
      console.log(`/simulation/delete/job error: [${jobId}] ${error}`);
      res.json({
        success: false,
        message: error.message
      });
    }
  });

  // 批量删除任务
  app.post('/simulation/delete/jobs', async (req, res) => {
    console.log(`delete jobs batch request: ${JSON.stringify(req.body)}`);
    const { businessIds } = req.body;
    const gatewayApi = util.getGatewayDeleteJobBatchByJobIdApi();
    try {
      const gatewayRes = await axios.post(gatewayApi, {
        jobIds: businessIds
      });

      const { success, message } = gatewayRes.data;
      if (success) {
        res.json({
          success: true
        });
      } else {
        res.json({
          success: false,
          message: message
        });
      }
    } catch (error) {
      console.log(`/simulation/delete/jobs error: [${businessIds}] ${error}`);
      res.json({
        success: false,
        message: error.message
      });
    }
  });

  // 暂停任务
  app.get('/simulation/pause/job/:businessId', async (req, res) => {
    const businessId = req.params.businessId;
    const gatewayApi = util.getGatewayPauseJobApi(businessId);
    try {
      const gatewayRes = await axios.get(gatewayApi);
      const { success, message } = gatewayRes.data;

      if (success) {
        res.json({
          success: true
        });
      } else {
        res.json({
          success: false,
          message: message
        });
      }
    } catch (error) {
      console.log(`/simulation/pause/job error: [${businessId}] ${error}`);
      res.json({
        success: false,
        message: error.message
      });
    }
  });

  app.get('/simulation/job/status', async (req, res) => {
    res.json({
      success: true,
      data: [
        { 'code': 'queuing', 'name': '排队中' },
        { 'code': 'running', 'name': '正在运行' },
        { 'code': 'succeed', 'name': '运行成功' },
        { 'code': 'failed', 'name': '运行失败' }
      ]
    });
  });

  // 下载仿真结果文件
  app.get('/simulation/result/:ossPath(*)', async (req, res) => {
    console.log('download simulation result', req.params);
    const { ossPath } = req.params;

    let tmpDir = util.isDesktop() ? `${process.env.tmp}/suanpan`.replace(/\\/g, '/') : '/tmp';

    const tmpFile = path.join(tmpDir, ossPath);
    await objectStorageService.fGetObject(configurationService.config.objectStorage.config.bucket, util.convertToUnixPath(ossPath), path.normalize(tmpFile));
    res.download(tmpFile);
  });

  // 仿真任务重试
  app.post('/simulation/job/retry/:jobId', async (req, res) => {
    const { jobId } = req.params;
    const axiosResponse = await axios.post(util.getSimJobRetryByJobId(jobId));
    const { success, message } = axiosResponse.data;
    res.json({
      success,
      message
    });
  });

  app.get('/simulation/param/download/:product/:type', async (req, res) => {
    const { product, type } = req.params;
    const filePath = path.join(__dirname, 'param', product, `${type}.csv`);

    console.log(`filePath = ${filePath}`);
    if (fs.existsSync(filePath)) {
      res.download(filePath);
    } else {
      res.json({
        success: false,
        message: `${product} ${type}.csv is not existed`
      });
    }
  });

  // 获取对象存储里项目文件列表
  app.get('/simulation/project/files', async (req, res) => {
    let projectFilePath = util.getOSSSimulationProjectFilePath();
    let objectItems = await objectStorageService.listObjects(configurationService.config.objectStorage.config.bucket, projectFilePath);
    // objectItems.forEach(item => {
    //   item['value'] = item['name'];
    //   item['name'] = path.basename(item['name']);
    // });
    res.json({
      success: true,
      data: objectItems
    });
  });

  app.get('/simulation/signUrl', async (req, res) => {
    console.log('sign url', req.params);
    const { key} = req.query;
    let signUrl = await objectStorageService.signatureUrl(configurationService.config.objectStorage.config.bucket, key);
    res.json({
      success: true,
      data: signUrl
    });
  });

  app.get('/simulation/jobSyncFiles/:jobId', async (req, res) => {
    const { jobId } = req.params;
    const key = util.getSimSyncPath(jobId);
    console.log(`get job(${jobId}) sync file list: ${key}`);
    let objects = await objectStorageService.listObjects(configurationService.config.objectStorage.config.bucket, key);
    // objects.forEach(o => {
    //   o.key = `${util.getSimSyncPath(jobId)}/${o.name}`
    // });
    res.json({
      success: true,
      data: objects
    });
  });

  // 获取当前上传进度
  app.get('/simulation/upload/progress', async (req, res) => {
    res.json({
      success: true,
      data: {
        progress: currentUploadProgress
      }
    });
  });

  // 工具函数：格式化文件大小
  function formatFileSize(bytes: number): string {
    if (bytes === 0) return '0 B';
    const k = 1024;
    const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
  }

  // 工具函数：格式化时间
  function formatTime(seconds: number): string {
    if (seconds === 0) return '0s';
    if (seconds < 60) return `${seconds}s`;
    if (seconds < 3600) return `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
    return `${Math.floor(seconds / 3600)}h ${Math.floor((seconds % 3600) / 60)}m`;
  }

}
